﻿/*
ParallelPlatform Project - A web platform to distribute and share knowledge
Online Editor - An online tool to create and play animations
Copyright (C) 2011 Daniel Guerrero Martínez & Sergio Rodríguez Lumley

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package Classes{

	import flash.display.MovieClip;
	//import flash.utils.getDefinitionByName;
	import flash.events.MouseEvent;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.ui.Mouse;
	import flash.ui.MouseCursor;
	import flash.net.FileReference;
	import flash.net.FileFilter;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLVariables;
	import flash.utils.ByteArray;
	
	/**
	* Multipart URL Loader
	*
	* Original idea by Marston Development Studio - http://marstonstudio.com/?p=36
	* @author Eugene Zatepyakin
	* @version 1.3
	* @link http://blog.inspirit.ru/
	* ----------------------------------------------------------------------------
	* Class MultipartURLLoader
	* Class MultipartURLLoaderEvent
	*
	* These external files use MIT License, compatible with GPLv3
	* Special thanks to Eugene Zatepyakin, owner of
	* - MultipartURLLoader
	* - MultipartURLLoaderEvent
	* For more information please visit http://code.google.com/p/in-spirit/
	*/
	import ru.inspirit.net.events.MultipartURLLoaderEvent;
	import ru.inspirit.net.MultipartURLLoader;
	
	public class Editor extends MovieClip {
		
		static public const VERSION:String = "1.0";
		
		// Static actions editor can run
		static public const LOAD:uint 		= 0;
		static public const RELOAD:uint 	= 1;
		static public const SET_AUTHOR:uint = 2;
		static public const FIRST_RUN:uint 	= 3;
		static public const PREVIEW:uint 	= 4;
		static public const SAVEONLINE:uint = 5;
		static public const DOWNLOAD:uint 	= 6;
		static public const SET_TITLE:uint 	= 7;
		static public const SET_CALLEE:uint = 8;
		static public const SET_BACKGROUND:uint = 9;
		
		// Editor runtime variables
		private var timelinereveal:Boolean=false;
		private var timelineshow:Boolean=false;
		private var timelinedragging:Boolean = false;
		private var canvaszoomout:Boolean=false;
		private var toolboxreveal:Boolean=false;
		private var menureveal:Boolean = false;
		private var menudragging:Boolean = false;
		private var mousex:Number; // Coordinates of mouse to avoid dragging everything
		private var mousey:Number;
		
		// Editor objects
		//private var timeline_mc; // Created graphicallly
		//private var toolbox_mc; // Created graphicallly
		//private var menu_mc; // Created graphicallly
		//private var undo_btn; // Created graphically
		//private var redo_btn; // Created graphically
		//private var notificator_mc:LoadedNotificator; // Created graphically
		private var canvas:Canvas;// Current canvas selected
		private var menu:Menu;
		private var previewer:PlayerAnimation;
		private var callee:String;
		
		// Used to select shapes in the toolbox and to show the timeline
		private var darkPanelTimeLine:DarkPanel;
		private var darkPanelToolBox:DarkPanel;
		private var darkPanelMenu:DarkerPanel;
		
		// Set the FileReference name
		private var fileRef:FileReference;
		private var initStatus:String;
		private var mloader:MultipartURLLoader; // MultiLoader to server

		/**
		Constructor
		*/
		public function Editor() {
			super();
			/** VARIABLE AND ACTIONS INITIALIZATION **/
			menu 					= new Menu();
			menu.setEditor(this);
			menu_mc.buttonMode 		= true;
			menu_mc.useHandCursor 	= true;
			menu_mc.onlineEnabled	= false;
			timeline_mc.y			= timeline_mc.y+100;
			timeline_mc.setParentFunction(showTimeLine);
			canvas 					= new Canvas();
			container_mc.addChild(canvas);
			container_mc.alpha 		= 0;
			previewer 				= null;
			tl_backArrow.alpha		= 0;
			tl_nextArrow.alpha		= 0;
			toolbox_mc.setToolFunction(function(tool:uint, shapeobj:String){
				canvas.toolToApply(tool, shapeobj);
			});
			canvas.setUndoRedoFunctions(undo_btn.putColor, redo_btn.putColor);
			canvas.setChangeToolFunction(toolbox_mc.changeTool);
			darkPanelTimeLine 		= new DarkPanel();
			darkPanelToolBox 		= new DarkPanel();
			darkPanelMenu 			= new DarkerPanel();
			
			fileRef 				= new FileReference()
			
			this.removeChild(timeline_mc);
			this.removeChild(menu_mc);
			this.removeChild(undo_btn);
			this.removeChild(redo_btn);
			this.removeChild(notificator_mc);
		}
		
		/**
		  Runs the specified action to the editor
		  @param action uint identifying the action to run (specified in this class as Static)
		  @param param String with all the (encrypted) information to parse as XML
		*/
		public function run(action:uint, param=null):void{
			
			switch(action){
				case LOAD:
					destroyInterface();
					// We clean the whole canvas
					canvas.createContent(0);
					
					// Get the content to load from file
					// Decrypt file
					// Load file
					initStatus = param as String;
					
					canvas.readXML(FileModule.stringToXML(FileModule.decompact(FileModule.decrypt(param as String))));
					
					// Remove the "new" dialog and build interface
					if(newDialog_mc.parent != null){
						this.removeChild(newDialog_mc);
					}
					this.constructInterface();
					break;
					
				case RELOAD:
					destroyInterface();
					// We clean the whole canvas
					canvas.createContent(0);
					if(initStatus != null && initStatus != ""){
						canvas.readXML(FileModule.stringToXML(FileModule.decompact(FileModule.decrypt(initStatus))));
						this.constructInterface(); 
					}else
						run(FIRST_RUN);
					break;
					
				case FIRST_RUN:
					destroyInterface();
					// We clean the whole canvas
					canvas.createContent(0);
					
					if(newDialog_mc.parent == null){
						this.addChild(newDialog_mc);
					}
					this.setChildIndex(newDialog_mc, numChildren-1);
					newDialog_mc.begin();
					break;
				
				case SET_AUTHOR:
					canvas.addAuthor(param as String);
					break;
				case SET_BACKGROUND:
					// For now, ignore this request, exists only to allow future
					// dynamic changing of the background, to make it more suitable
					// according to web design
					break;
				case SET_CALLEE:
					callee = param as String;
					menu.enableOnline();
					break;
					
				case SET_TITLE:
					canvas.setTitle(param[0]);
					canvas.setDescription(param[1]);
					canvas.setDelay(param[2]);
					break;
					
				case DOWNLOAD:
					openSaveDialog(FileModule.encrypt(FileModule.compact(canvas.writeXML())));
					break;
				case SAVEONLINE:
					//canvas.printActions();
					sendFileToServer();
					break;
					
				case PREVIEW:
					if(previewer == null) previewer = new PlayerAnimation();
					previewer.setXMLfile(new XML(canvas.writeXML()));
					previewer.begin();
					this.addChild(previewer);
					this.addChild(new PreviewExiter(quitPreviewer));
					break;
			}
		}
		
		/**
		  Creates all the necessary content to start the editor
		  @param data Array with the data inside, following the next order:
		  	String newTitle, String newDescription, uint newLength, Number newDelay
		*/
		public function createContent(array:Array):void{
			// Build interface
			if(array != null){
				canvas.setTitle(array[0]);
				canvas.setDescription(array[1]);
				canvas.createContent(array[2]);
				canvas.setDelay(array[3]);
			}
			this.constructInterface();
		}
		
		/**
		  Opens a dialog to load an animation stored locally
		*/
		public function openLoadDialog():void{
			var fileFilter:FileFilter = new FileFilter("Animation (*.plpanim)", "*.plpanim");
			var allTypes:Array = new Array(fileFilter);
			// Remove everything from it
			fileRef.cancel();
			fileRef.removeEventListener(Event.SELECT, fileSelected);
			// Add event listeners for its various fileRef functions below
			fileRef.addEventListener(Event.SELECT, fileSelected);
			fileRef.browse(allTypes);
		}
		
		/**
		  Opens a dialog to save an animation to local
		  @param str String with the data to save
		*/
		public function openSaveDialog(str:String):void{
			var ttl:String = canvas.getTitle();
			if(ttl == null || ttl.length <= 0) ttl = "newPlpAnimation";
			fileRef.cancel();
			fileRef.removeEventListener(Event.SELECT, fileSelected);
			fileRef.save(str, ttl+".plpanim");
		}

		/********** Private functions *********/
		
		/**
		  Builds the interface of the editor, presumably after dialogs and/or 
		  errors have been shown and objects have been built (canvas is completed)
		*/
		private function constructInterface():void{
			
			container_mc.scaleX = 0.5;
			container_mc.scaleY = 0.5;
			container_mc.x = 350 - 350*container_mc.scaleX;
			container_mc.y = 250 - 250*container_mc.scaleY;
			container_mc.addChild(new CanvasBackground());
			container_mc.setChildIndex(canvas, container_mc.numChildren-1);
			timeline_mc.displaySelectedContainer(0);
			checkScenesArrows();
			// Set the order of children as we wish
			// 1: Menu
			// 2: TimeLine
			// 3: ToolBox
			this.addChild(timeline_mc);
			this.addChild(menu_mc);
			this.addChild(undo_btn);
			this.addChild(redo_btn);

			/** ADDING EVENTS **/
			this.addEventListener(Event.ENTER_FRAME, canvasInitializationRefresh);
			// All events moved to canvasInitializationRefresh
			// This event is added after initialization event!
			//this.addEventListener(Event.ENTER_FRAME, canvasRefresh);
		}
		
		/**
		  Removes all listeners of the editor and leaves it exactly as first run, this
		  is a safe procedure as it checks for errors by itself
		*/
		private function destroyInterface():void{
			try{
				this.removeChild(timeline_mc);
				this.removeChild(menu_mc);
				this.removeChild(undo_btn);
				this.removeChild(redo_btn);
				container_mc.removeChildAt(1); // Remove canvasBackground
			}catch(err:Error){
				//trace("Error: timeline_mc was no child of this");
				// This error has absolutely no consecuences, it is safe
				// to continue
			}
			if(darkPanelMenu.parent != null){
				this.showMenu(); // Show menu will hide it if it detects darkPanelMenu was being shown
			}
			container_mc.alpha = 0;
			tl_backArrow.alpha = 0;
			tl_nextArrow.alpha = 0;
			this.removeEventListener(Event.ENTER_FRAME, canvasInitializationRefresh);
			this.removeEventListener(Event.ENTER_FRAME, canvasRefresh);
			timeline_mc.removeEventListener(MouseEvent.MOUSE_OVER, revealTimeLine);
			timeline_mc.removeEventListener(MouseEvent.MOUSE_OUT, revealTimeLine);
			timeline_mc.removeEventListener(MouseEvent.CLICK, showTimeLine);
			timeline_mc.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownTimeLine);

			toolbox_mc.removeEventListener(MouseEvent.MOUSE_OVER, revealToolBox);
			toolbox_mc.removeEventListener(MouseEvent.MOUSE_OUT, revealToolBox);
			toolbox_mc.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownToolbox);
			
			menu_mc.removeEventListener(MouseEvent.MOUSE_OVER, revealMenu);
			menu_mc.removeEventListener(MouseEvent.MOUSE_OUT, revealMenu);
			menu_mc.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownMenu);
			undo_btn.removeEventListener(MouseEvent.MOUSE_OVER, revealMenu);
			undo_btn.removeEventListener(MouseEvent.MOUSE_OUT, revealMenu);
			undo_btn.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownMenu);
			redo_btn.removeEventListener(MouseEvent.MOUSE_OVER, revealMenu);
			redo_btn.removeEventListener(MouseEvent.MOUSE_OUT, revealMenu);
			redo_btn.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownMenu);
			menu_mc.removeEventListener(MouseEvent.CLICK, showMenu);
			
			tl_backArrow.removeEventListener(MouseEvent.CLICK, previousContainer);
			tl_nextArrow.removeEventListener(MouseEvent.CLICK, nextContainer);
			
			stage.removeEventListener(KeyboardEvent.KEY_DOWN, keyPressed);
		}

		/**
		Periodic function that refreshes the whole interface, for animations
		*/
		private function canvasRefresh(evt:Event):void {
			/** Canvas issues **/
			if (canvaszoomout) {
				if(container_mc.scaleX != 0.75 || container_mc.scaleY != 0.75){
					container_mc.scaleX = Math.max(0.75, container_mc.scaleX-0.02);
					container_mc.scaleY = Math.max(0.75, container_mc.scaleY-0.02);
					
					container_mc.x = 350 - 350*container_mc.scaleX;
					container_mc.y = 50 - 50*container_mc.scaleY;
				}
			} else {
				if(container_mc.scaleX != 1 || container_mc.scaleY != 1){
					container_mc.scaleX = Math.min(1, container_mc.scaleX+0.02);
					container_mc.scaleY = Math.min(1, container_mc.scaleY+0.02);
					
					container_mc.x = 350 - 350*container_mc.scaleX;
					container_mc.y = 50 - 50*container_mc.scaleY;
				}
			}

			/** TimeLine issues **/
			if (timelinereveal) {
				if(timeline_mc.alpha != 1)
					timeline_mc.alpha=Math.min(1,timeline_mc.alpha+0.15);
			} else {
				if(timeline_mc.alpha != 0.3)
					timeline_mc.alpha=Math.max(0.3,timeline_mc.alpha-0.15);
			}

			if (timelineshow) {
				if(timeline_mc.y != 400)
					timeline_mc.y = Math.max(400, timeline_mc.y+ (399.5 - timeline_mc.y)*0.1);
			} else {
				if(timeline_mc.y != 500)
					timeline_mc.y = Math.min(500, timeline_mc.y+ (500.5 - timeline_mc.y)*0.25);
			}

			/** Toolbox issues **/
			toolbox_mc.refreshPanel(toolboxreveal);
			
			/** Menu issues **/
			if(menureveal){
				if(menu_mc.alpha != 1){
					menu_mc.alpha=Math.min(1,menu_mc.alpha+0.15);
					redo_btn.alpha = menu_mc.alpha;
					undo_btn.alpha = menu_mc.alpha;
				}
			}else{
				if(menu_mc.alpha != 0.3){
					menu_mc.alpha=Math.max(0.3,menu_mc.alpha-0.15);
					redo_btn.alpha = menu_mc.alpha;
					undo_btn.alpha = menu_mc.alpha;
				}
			}

		}
		
		/**
		@brief Reveals the toolbox
		*/
		private function revealToolBox(evt:MouseEvent):void{
			if (evt.type==MouseEvent.MOUSE_OVER) {
				toolboxreveal=true;
			} else {
				toolboxreveal=false;
			}
		}
		
		/**
		@brief Reveals the menu
		*/
		private function revealMenu(evt:MouseEvent):void{
			if (evt.type==MouseEvent.MOUSE_OVER) {
				menureveal=true;
			} else {
				menureveal=false;
			}
		}

		/**
		Sets or unsets the TimeLine object to be revealed or not
		*/
		private function revealTimeLine(evt:MouseEvent) {
			if (evt.type==MouseEvent.MOUSE_OVER) {
				timelinereveal=true;
			} else {
				timelinereveal=false;
			}
		}
		
		/**
		Sets or unsets the TimeLine object to be shown or not
		*/
		private function showTimeLine(evt:MouseEvent):void{
			if( !timelinedragging){
				// Here we hide the time line
				if (timelineshow) {
					timelineshow	=false;
					timelinereveal	=false;
					canvaszoomout	=false;
					timeline_mc.addEventListener(MouseEvent.MOUSE_OVER, revealTimeLine);
					timeline_mc.addEventListener(MouseEvent.MOUSE_OUT, revealTimeLine);
					timeline_mc.addEventListener(MouseEvent.CLICK, showTimeLine);
					timeline_mc.addEventListener(MouseEvent.MOUSE_DOWN, dragStartTimeLine);
					
					if(timeline_mc.hasChanged()){
						//Resize the number of containers in canvas
						canvas.setNumContainers(timeline_mc.getNumContainers());
						canvas.selectContainer(timeline_mc.getSelectedContainer());
						canvas.setContainersTypes(timeline_mc.getContainersTypes());
						checkScenesArrows();
					}
					
					timeline_mc.cleanAll();
					
					// Delete dark semi-transparent square behind timeline and
					//this.removeChild(darkPanelTimeLine);
					darkPanelTimeLine.endShow();
					// remove the click listener to do it with showTimeEvent
					darkPanelTimeLine.removeEventListener(MouseEvent.CLICK, showTimeLine);
	
				} else {// Here we show the time line
					timelineshow=true;
					canvaszoomout=true;
					timelinereveal=true;
					timeline_mc.removeEventListener(MouseEvent.MOUSE_OVER, revealTimeLine);
					timeline_mc.removeEventListener(MouseEvent.MOUSE_OUT, revealTimeLine);
					timeline_mc.removeEventListener(MouseEvent.CLICK, showTimeLine);
					timeline_mc.removeEventListener(MouseEvent.MOUSE_DOWN, dragStartTimeLine);
					
					// Put timeline over ToolBox
					//this.setChildIndex(timeline_mc, this.getChildIndex(toolbox_mc)+1);
					// Create dark semi-transparent square behind timeline and
					this.addChildAt(darkPanelTimeLine, this.getChildIndex(timeline_mc));
					darkPanelTimeLine.beginShow();
					// add the click listener to it with showTimeEvent
					darkPanelTimeLine.addEventListener(MouseEvent.CLICK, showTimeLine);
					
					timeline_mc.setContainers(canvas.getContainers(), canvas.getCurrentContainerIndex());
				}
			}
			timelinedragging = false;
		}
		
		/**
		  Shows or hides the menu according to parameter
		  @param evt MouseEvent that indicates if it has to show the menu (by
		  pressing over the menu_mc) or hide it, in all other cases
		*/
		private function showMenu(evt:MouseEvent = null):void{
			if(! menudragging){
				if(evt != null && evt.target == menu_mc){ // Show the menu
					darkPanelMenu.addEventListener(MouseEvent.CLICK, showMenu);
					this.addChild(darkPanelMenu);
					this.addChild(menu);
					menu.setTitle(canvas.getTitle());
					menu.setDescription(canvas.getDescription());
					menu.setDelay(canvas.getDelay());
					menu.beginShow();
					
					if(callee != null && callee != ""){
						menu.enableOnline(true);
					}
					
					darkPanelMenu.beginShow();
					//canvaszoomout = true;
				}else{ // Hide the menu
					darkPanelMenu.removeEventListener(MouseEvent.CLICK, showMenu);
					darkPanelMenu.endShow();
					menu.endShow();
					//if(! timelineshow) canvaszoomout = false;
				}
			}
			menudragging = false;
		}
		
		
		/**
		  Displays the animation efect of creation at the beginning, after it,
		  it initializes all listeners in the editor
		*/
		private function canvasInitializationRefresh(evt:Event):void{
			container_mc.scaleX = Math.min(1, container_mc.scaleX+0.04);
			container_mc.scaleY = Math.min(1, container_mc.scaleY+0.04);
			container_mc.x 		= 350 - 350*container_mc.scaleX;
			container_mc.y 		= 250 - 250*container_mc.scaleY;
			container_mc.alpha 	= Math.min(1, container_mc.alpha+0.05);
			tl_backArrow.alpha	= Math.min(1, tl_backArrow.alpha+0.05);
			tl_nextArrow.alpha	= Math.min(1, tl_nextArrow.alpha+0.05);
			if(container_mc.scaleX == 1 && container_mc.alpha == 1){
				
				this.removeEventListener(Event.ENTER_FRAME, canvasInitializationRefresh);
				
				// Adding all the eventListeners
				this.addEventListener(Event.ENTER_FRAME, canvasRefresh);
				timeline_mc.addEventListener(MouseEvent.MOUSE_OVER, revealTimeLine);
				timeline_mc.addEventListener(MouseEvent.MOUSE_OUT, revealTimeLine);
				timeline_mc.addEventListener(MouseEvent.CLICK, showTimeLine);
				timeline_mc.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownTimeLine);
	
				toolbox_mc.addEventListener(MouseEvent.MOUSE_OVER, revealToolBox);
				toolbox_mc.addEventListener(MouseEvent.MOUSE_OUT, revealToolBox);
				toolbox_mc.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownToolbox);
				
				menu_mc.addEventListener(MouseEvent.MOUSE_OVER, revealMenu);
				menu_mc.addEventListener(MouseEvent.MOUSE_OUT, revealMenu);
				menu_mc.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownMenu);
				menu_mc.addEventListener(MouseEvent.CLICK, showMenu);
				undo_btn.addEventListener(MouseEvent.MOUSE_OVER, revealMenu);
				undo_btn.addEventListener(MouseEvent.MOUSE_OUT, revealMenu);
				undo_btn.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownMenu);
				undo_btn.addEventListener(MouseEvent.CLICK, undoAction);
				redo_btn.addEventListener(MouseEvent.MOUSE_OVER, revealMenu);
				redo_btn.addEventListener(MouseEvent.MOUSE_OUT, revealMenu);
				redo_btn.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownMenu);
				redo_btn.addEventListener(MouseEvent.CLICK, redoAction);
				
				tl_backArrow.addEventListener(MouseEvent.CLICK, previousContainer);
				tl_nextArrow.addEventListener(MouseEvent.CLICK, nextContainer);
				
				stage.addEventListener(KeyboardEvent.KEY_DOWN, keyPressed);
			}
		}
		
		/**
		  Detects when a key has been pressed
		*/
		private function keyPressed(evt:KeyboardEvent):void{
			var captured:Boolean = false;
			if(evt.ctrlKey){
				switch(String.fromCharCode(evt.charCode)){
					case 'z':
						captured = true;
						undoAction();
						break;
					case 'y':
						captured = true;
						redoAction();
						break;
					case 's': // Save!
						captured = true;
						// If canvas is added to the Editor, it means
						// that we are using it, so it is possible to
						// save whatever we have
						if(this.canvas.parent == this){
							this.run(DOWNLOAD);
						}
						break;
				}
			}
			if(!captured){
				canvas.keyPressed(evt);
			}
		}
		
		/**
		  Sends to the canvas the action of undo an action
		*/
		private function undoAction(evt=null):void{
			if(! menudragging && undo_btn.isEnabled()){
				canvas.undoAction();
			}
		}
		
		/**
		  Sends to the canvas the action of redo an action
		*/
		private function redoAction(evt=null):void{
			if(! menudragging && redo_btn.isEnabled()){
				canvas.redoAction();
			}
		}
		
		/**
		  Mouse was not dragged, we stop reading these events
		*/
		private function mouseNotDragged(evt:MouseEvent):void{
			stage.removeEventListener(MouseEvent.MOUSE_UP, mouseNotDragged);
			
			this.removeEventListener(MouseEvent.MOUSE_MOVE, dragStartTimeLine);
			this.removeEventListener(MouseEvent.MOUSE_MOVE, dragStartToolbox);
			this.removeEventListener(MouseEvent.MOUSE_MOVE, dragStartMenu);
		}
		
		/**
		  Checker if user does not want to drag Timeline
		*/
		private function mouseDownTimeLine(evt:MouseEvent):void{
			mousex = evt.stageX;
			mousey = evt.stageY;
			this.addEventListener(MouseEvent.MOUSE_MOVE, dragStartTimeLine);
			stage.addEventListener(MouseEvent.MOUSE_UP, mouseNotDragged);
		}
		
		/**
		  Starts draging the toolbox with mouse
		*/
		private function dragStartTimeLine(evt:MouseEvent):void{
			if((mousex < 0 && mousey < 0) ||
				(Math.abs(evt.stageX - mousex) > 15 ||
				 Math.abs(evt.stageY - mousey) > 15)){
				
				if(mousex >= 0){
					mousex = -1;
					mousey = -1;
					stage.removeEventListener(MouseEvent.MOUSE_UP, mouseNotDragged);
				}
				this.removeEventListener(MouseEvent.MOUSE_MOVE, dragStartTimeLine);
				this.addEventListener(MouseEvent.MOUSE_MOVE, moveTimeLine);
				this.addEventListener(MouseEvent.MOUSE_UP, dragStopTimeLine);
				Mouse.cursor = MouseCursor.HAND;
			}
		}
		
		private function moveTimeLine(evt:MouseEvent):void{
			timelinedragging = true;
			timeline_mc.tagPicker_mc.x = evt.stageX-timeline_mc.tagPicker_mc.width/2 -350;
			if(timeline_mc.tagPicker_mc.x < -330)
				timeline_mc.tagPicker_mc.x = -330;
			else if(timeline_mc.tagPicker_mc.x > 330-timeline_mc.tagPicker_mc.width)
				timeline_mc.tagPicker_mc.x = 330-timeline_mc.tagPicker_mc.width;
			tl_backArrow.x = timeline_mc.x + timeline_mc.tagPicker_mc.x - 19;
			tl_nextArrow.x = timeline_mc.x + timeline_mc.tagPicker_mc.x + timeline_mc.tagPicker_mc.width + 15;
		}
		
		/**
		  Stops draging the toolbox with mouse
		*/
		private function dragStopTimeLine(evt:MouseEvent):void{
			this.removeEventListener(MouseEvent.MOUSE_MOVE, moveTimeLine);
			this.removeEventListener(MouseEvent.MOUSE_UP, dragStopTimeLine);
			Mouse.cursor = MouseCursor.AUTO;
		}
		
		/**
		  Checker if user does not want to drag Menu
		*/
		private function mouseDownMenu(evt:MouseEvent):void{
			mousex = evt.stageX;
			mousey = evt.stageY;
			this.addEventListener(MouseEvent.MOUSE_MOVE, dragStartMenu);
			stage.addEventListener(MouseEvent.MOUSE_UP, mouseNotDragged);
		}
		
		
		/**
		  Starts draging the toolbox with mouse
		*/
		private function dragStartMenu(evt:MouseEvent):void{
			if((mousex < 0 && mousey < 0) ||
				(Math.abs(evt.stageX - mousex) > 15 ||
				 Math.abs(evt.stageY - mousey) > 15)){
				
				if(mousex >= 0){
					mousex = -1;
					mousey = -1;
					stage.removeEventListener(MouseEvent.MOUSE_UP, mouseNotDragged);
				}
				this.removeEventListener(MouseEvent.MOUSE_MOVE, dragStartMenu);
				this.addEventListener(MouseEvent.MOUSE_MOVE, moveMenu);
				this.addEventListener(MouseEvent.MOUSE_UP, dragStopMenu);
				Mouse.cursor = MouseCursor.HAND;
			}
		}
		
		/**
		  Moves the main menu with the mouse
		*/
		private function moveMenu(evt:MouseEvent):void{
			menudragging = true;
			
			// Move the main menu button
			menu_mc.x = evt.stageX-menu_mc.width/2;
			if(menu_mc.x < 0)
				menu_mc.x = 0;
								
			else if(menu_mc.x > 700-menu_mc.width)
				menu_mc.x = 700-menu_mc.width;
			
			// Now that the main menu button is set, move the others
			undo_btn.x = menu_mc.x + menu_mc.width - 2;
			redo_btn.x = undo_btn.x + undo_btn.width - 2;
			
			if(redo_btn.x > 700 - redo_btn.width){
				redo_btn.x = menu_mc.x - redo_btn.width + 2;
				undo_btn.x = redo_btn.x - undo_btn.width + 2;
			}
		}
		
		/**
		  Stops draging the toolbox with mouse
		*/
		private function dragStopMenu(evt:MouseEvent):void{
			this.removeEventListener(MouseEvent.MOUSE_MOVE, moveMenu);
			this.removeEventListener(MouseEvent.MOUSE_UP, dragStopMenu);
			Mouse.cursor = MouseCursor.AUTO;
		}
		
		/**
		  Checker if user does not want to drag Toolbox
		*/
		private function mouseDownToolbox(evt:MouseEvent):void{
			mousex = evt.stageX;
			mousey = evt.stageY;
			this.addEventListener(MouseEvent.MOUSE_MOVE, dragStartToolbox);
			stage.addEventListener(MouseEvent.MOUSE_UP, mouseNotDragged);
		}
		
		/**
		  Starts draging the toolbox with mouse
		*/
		private function dragStartToolbox(evt:MouseEvent):void{
			if((mousex < 0 && mousey < 0) ||
				(Math.abs(evt.stageX - mousex) > 15 ||
				 Math.abs(evt.stageY - mousey) > 15)){
				
				if(mousex >= 0){
					mousex = -1;
					mousey = -1;
					stage.removeEventListener(MouseEvent.MOUSE_UP, mouseNotDragged);
				}
				this.removeEventListener(MouseEvent.MOUSE_MOVE, dragStartToolbox);
				this.addEventListener(MouseEvent.MOUSE_MOVE, moveToolbox);
				this.addEventListener(MouseEvent.MOUSE_UP, dragStopToolbox);
				Mouse.cursor = MouseCursor.HAND;
			}
		}
		
		/**
		  Move the toolbox with the mouse
		*/
		private function moveToolbox(evt:MouseEvent):void{
			toolbox_mc.moveY(evt.stageY);
		}
		
		/**
		  Stops draging the toolbox with mouse
		*/
		private function dragStopToolbox(evt:MouseEvent):void{
			this.removeEventListener(MouseEvent.MOUSE_MOVE, moveToolbox);
			this.removeEventListener(MouseEvent.MOUSE_UP, dragStopToolbox);
			Mouse.cursor = MouseCursor.AUTO;
		}
		
		/**
		  Selects the previous scene in canvas respect the current one
		*/
		private function previousContainer(evt:MouseEvent):void{
			if(canvas.getCurrentContainerIndex() > 0){
				canvas.selectContainer(canvas.getCurrentContainerIndex()-1);
				timeline_mc.displaySelectedContainer(canvas.getCurrentContainerIndex());
			}
			
			checkScenesArrows();
		}
		
		/**
		  Selects the next scene in canvas respect the current one
		*/
		private function nextContainer(evt:MouseEvent):void{
			if(canvas.getCurrentContainerIndex() < canvas.getNumContainers()-1){
				canvas.buildContainers();
				canvas.selectContainer(canvas.getCurrentContainerIndex()+1);
				timeline_mc.displaySelectedContainer(canvas.getCurrentContainerIndex());
			}
			
			checkScenesArrows();
		}
		
		private function checkScenesArrows():void{
			if(canvas.getCurrentContainerIndex() >= canvas.getNumContainers()-1){
				tl_nextArrow.makeVisible(false);
			}else{
				tl_nextArrow.makeVisible(true);
			}
			
			if(canvas.getCurrentContainerIndex() <= 0){
				tl_backArrow.makeVisible(false);
			}else{
				tl_backArrow.makeVisible(true);
			}
		}
		
		/**
		  A file has been selected by the open dialog, automatically opens
		  the file for the editor
		  @param evt Event of the selected file
		*/
		private function fileSelected(evt:Event):void{
			fileRef.addEventListener(Event.COMPLETE, fileCompleted);
			fileRef.removeEventListener(Event.SELECT, fileSelected);
			//fileRef.addEventListener(ProgressEvent.PROGRESS, progressHandler);
			fileRef.load();
		}
		
		/**
		  The selected file has been loaded
		*/
		private function fileCompleted(evt:Event):void{
			//trace(fileRef.data);
			fileRef.removeEventListener(Event.COMPLETE, fileCompleted);
			this.run(LOAD, fileRef.data.toString());
		}
		
		/**
		  Removes the previewer from stage
		*/
		private function quitPreviewer():void{
			previewer.halt();
			this.removeChild(previewer);
		}
		
		// ---------------- SENDING FILES TO SERVER --------------------//
		/**
		  Sends the animation to the stored server (passed to flash animation by parameters)
		  Note that it is not checked here if callee exists at all
		*/
		private function sendFileToServer():void{
			var file:String = FileModule.encrypt(FileModule.compact(canvas.writeXML()));
			var data1:ByteArray = new ByteArray();
			data1.writeUTFBytes(file);
			data1.position = 0;
			
			mloader = new MultipartURLLoader();
			mloader.dataFormat = URLLoaderDataFormat.TEXT;
			
			mloader.addEventListener(Event.COMPLETE, sentFileToServer);
			mloader.addEventListener(IOErrorEvent.IO_ERROR, onError);
			mloader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
			mloader.addEventListener(MultipartURLLoaderEvent.DATA_PREPARE_PROGRESS, onWrite);
			mloader.addEventListener(MultipartURLLoaderEvent.DATA_PREPARE_COMPLETE, onWriteEnd);
			
			// Prepare data
			mloader.addVariable('lastupload', true);
			mloader.addFile(data1, 'text.plpanim', 'file', 'text/plain');
			
			//debug
			//text_txt = new TextField(); text_txt.x = 100; text_txt.y = 100; text_txt.width = 400; text_txt.height = 200;
			//this.addChild(text_txt);
			
			notificator_mc.begin();
			this.addChild(notificator_mc);
			mloader.load(callee, true);
		}
		
		private function onWrite(evt:MultipartURLLoaderEvent):void{
			//text_txt.text = 'Prepare data: ' + evt.bytesTotal + '/' + evt.bytesWritten;
		}
		
		private function onWriteEnd(evt:MultipartURLLoaderEvent):void {
			mloader.removeEventListener(MultipartURLLoaderEvent.DATA_PREPARE_PROGRESS, onWrite);
			mloader.removeEventListener(MultipartURLLoaderEvent.DATA_PREPARE_COMPLETE, onWriteEnd);
			if(mloader.PREPARED){
				mloader.startLoad();
			}
		}
		
		private function onError(evt:Event):void{
			notificator_mc.finish(false);
			
			mloader.removeEventListener(Event.COMPLETE, sentFileToServer);
			mloader.removeEventListener(IOErrorEvent.IO_ERROR, onError);
			mloader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
			mloader.removeEventListener(MultipartURLLoaderEvent.DATA_PREPARE_PROGRESS, onWrite);
			mloader.removeEventListener(MultipartURLLoaderEvent.DATA_PREPARE_COMPLETE, onWriteEnd);
		}
		
		/**
		  The file has been sent to the server
		*/
		private function sentFileToServer(evt:Event):void{

			if(String(mloader.loader.data).indexOf("true") >= 0){ // We asume if true exists, then everything went Ok
				notificator_mc.finish(true);
			}else{
				notificator_mc.finish(false);
			}
			
			mloader.removeEventListener(Event.COMPLETE, sentFileToServer);
			mloader.removeEventListener(IOErrorEvent.IO_ERROR, onError);
			mloader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
			mloader.removeEventListener(MultipartURLLoaderEvent.DATA_PREPARE_PROGRESS, onWrite);
			mloader.removeEventListener(MultipartURLLoaderEvent.DATA_PREPARE_COMPLETE, onWriteEnd);
		}
		
	}
}